/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cjt2325.cameralibrary.util;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorValue;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Value Animator
 */
public class ValueAnimator {
    private AnimatorGroup mAnimationGroup = new AnimatorGroup();
    private AnimatorUpdateListener mListener;
    private State mState = State.ANIM_NULL;
    private AnimatorStatusChangeListener mAnimatorStatusChangeListener;

    /**
     * ValueAnimator
     *
     * @param values float
     */
    private ValueAnimator(float... values) {
        mAnimationGroup.clear();
        Set<AnimatorValue> setQueue = new LinkedHashSet<>();
        if (values != null) {
            float[] floats = values.clone();
            if (floats.length == 1) {
                float start = 0;
                float end = floats[0];
                setQueue.add(getAnimatorValue(new AnimFloat(start, end)));
            }

            for (int i = 0; i < floats.length; i++) {
                if (i == floats.length - 1) {
                    break;
                }
                float start = floats[i];
                float end = floats[i + 1];
                setQueue.add(getAnimatorValue(new AnimFloat(start, end)));
            }
        }
        if (setQueue.size() == 0) {
            return;
        }
        AnimatorValue[] animatorValues = new AnimatorValue[setQueue.size()];
        setQueue.toArray(animatorValues);
        mAnimationGroup.runSerially(animatorValues);
    }

    private AnimatorValue getAnimatorValue(AnimFloat animFloat) {
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(
                (animatorValue1, v) -> {
                    float value = animFloat.getStartF() + (animFloat.getEndF() - animFloat.getStartF()) * v;
                    if (mListener != null) {
                        mListener.onAnimationUpdate(this, value);
                    }
                });
        return animatorValue;
    }

    /**
     * start
     */
    public void start() {
        mAnimationGroup.start();
    }

    /**
     * setDuration
     *
     * @param mill long
     */
    public void setDuration(long mill) {
        mAnimationGroup.setDuration(mill);
    }

    /**
     * ofFloat
     *
     * @param values float
     * @return ValueAnimator
     */
    public static ValueAnimator ofFloat(float... values) {
        return new ValueAnimator(values);
    }

    /**
     * getAnimatorValue
     *
     * @return Animator[]
     */
    public Animator[] getAnimatorValue() {
        List<Animator> list = new ArrayList<>();
        int count = mAnimationGroup.getRoundCount();
        for (int i = 0; i < count; i++) {
            list.addAll(mAnimationGroup.getAnimatorsAt(i));
        }
        if (list.size() == 0) {
            return new Animator[0];
        }
        Animator[] animators = new Animator[list.size()];
        list.toArray(animators);
        return animators;
    }

    private void addAnimatorStatusChangeListener() {
        mAnimationGroup.setStateChangedListener(
                new Animator.StateChangedListener() {
                    @Override
                    public void onStart(Animator animator) {
                        setState(State.ANIM_START);
                    }

                    @Override
                    public void onStop(Animator animator) {
                        setState(State.ANIM_STOP);
                    }

                    @Override
                    public void onCancel(Animator animator) {
                        setState(State.ANIM_CANCEL);
                    }

                    @Override
                    public void onEnd(Animator animator) {
                        setState(State.ANIM_END);
                    }

                    @Override
                    public void onPause(Animator animator) {
                        setState(State.ANIM_PAUSE);
                    }

                    @Override
                    public void onResume(Animator animator) {
                        setState(State.ANIM_RESUME);
                    }
                });
    }

    private void setState(State state) {
        this.mState = state;
        if (state == State.ANIM_CANCEL
                || state == State.ANIM_END
                || state == State.ANIM_PAUSE
                || state == State.ANIM_STOP) {
            if (mAnimatorStatusChangeListener != null) {
                mAnimatorStatusChangeListener.onEnd(this, state);
            }
        } else if (state == State.ANIM_START || state == State.ANIM_RESUME) {
            if (mAnimatorStatusChangeListener != null) {
                mAnimatorStatusChangeListener.start(this, state);
            }
        } else {
            LogUtil.error(LogUtil.DEFAULT_TAG, "setState state:" + state);
        }
    }

    /**
     * getAnimationState
     *
     * @return State
     */
    public State getAnimationState() {
        return mState;
    }

    /**
     * addUpdateListener
     *
     * @param listener AnimatorUpdateListener
     */
    public void addUpdateListener(AnimatorUpdateListener listener) {
        this.mListener = listener;
    }

    /**
     * addListener
     *
     * @param listener AnimatorStatusChangeListener
     */
    public void addListener(AnimatorStatusChangeListener listener) {
        this.mAnimatorStatusChangeListener = listener;
        addAnimatorStatusChangeListener();
    }

    /**
     * AnimatorUpdateListener
     */
    public interface AnimatorUpdateListener {
        void onAnimationUpdate(ValueAnimator animation, Object value);
    }

    /**
     * Animator Status Change Listener
     */
    public interface AnimatorStatusChangeListener {
        void onEnd(ValueAnimator animator, State state);

        void start(ValueAnimator animator, State state);
    }

    /**
     * animation status
     */
    public enum State {
        ANIM_NULL,
        ANIM_START,
        ANIM_STOP,
        ANIM_CANCEL,
        ANIM_END,
        ANIM_PAUSE,
        ANIM_RESUME
    }

    private static class AnimFloat {
        private float startF;
        private float endF;

        private AnimFloat(float startF, float endF) {
            this.startF = startF;
            this.endF = endF;
        }

        private float getStartF() {
            return startF;
        }

        private float getEndF() {
            return endF;
        }
    }
}
